package org.sja.log2traffic;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.mockito.Mockito;
import org.sja.log2traffic.dataStructures.CategoryTrackerEntity;
import org.sja.log2traffic.dataStructures.ConfigurationEntity;
import org.sja.log2traffic.dataStructures.ILogData;
import org.sja.log2traffic.dataStructures.ProcessingStatusEntity;
import org.sja.log2traffic.dataStructures.ResultValue;
import org.sja.log2traffic.dataStructures.simpleTime.LogDataSimpleTime;
import org.sja.log2traffic.dataStructures.timeAgregator.LogDataTimeAgregator;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class TimeAgregatorCalculator {

	private IDateTimeFormatter dateTimeFormatter = new DataCalculator();
	private ITimeAgregatorCalculator timeAgregatorCalculator = new DataCalculator();
	private ISimpleTimeCalculator simpleTimeCalculator = new DataCalculator();
	private LogAnalyzer logAnalyzer = new LogAnalyzer();
	private ConfigurationManipulator configurationManipulator = new ConfigurationManipulator(dateTimeFormatter);
	private DemoTrafficGenerator demoTrafficGenerator = new DemoTrafficGenerator();
	private IStatusOutputer statusOutputer = Mockito.mock(IStatusOutputer.class);
	
	int amountOfAssertions = 0;

	private Map<String, Map<DateTime, Long>> agregatedHours;
	private Map<String, Map<DateTime, Long>> agregatedDays;
	private Map<String, Map<DateTime, Long>> agregatedWeeks;
	private CategoryTrackerEntity categoryTracker;

	ConfigurationEntity configurationForWeeks;
	ConfigurationEntity configurationForDays;
	ConfigurationEntity configurationForHours;

	LogDataTimeAgregator logDataForWeeksUnderTest;
	LogDataTimeAgregator logDataForDaysUnderTest;
	LogDataSimpleTime logDataForHoursUnderTest;
	
	
	
	@BeforeClass
	public void setupClass() {

		agregatedHours = new HashMap<String, Map<DateTime, Long>>();
		agregatedDays = new HashMap<String, Map<DateTime, Long>>();
		agregatedWeeks = new HashMap<String, Map<DateTime, Long>>();

		categoryTracker = new CategoryTrackerEntity();

		configurationForWeeks = configurationManipulator.getConfigurationForWeeksReport("testResources/log2trafficForTest.properties");
		configurationForDays = configurationManipulator.getConfigurationForDaysReport("testResources/log2trafficForTest.properties ");
		configurationForHours = configurationManipulator.getConfigurationForHoursReport("testResources/log2trafficForTest.properties ");
		
		System.out.println("Generating test data ...");
		demoTrafficGenerator.generateFiles(5, configurationForWeeks.getInputDirectories()[0], configurationForWeeks.getInputDirectories()[1], agregatedWeeks, agregatedDays, agregatedHours);

		logDataForWeeksUnderTest = new LogDataTimeAgregator(configurationForWeeks, timeAgregatorCalculator);
		logDataForDaysUnderTest = new LogDataTimeAgregator(configurationForDays, timeAgregatorCalculator);
		logDataForHoursUnderTest = new LogDataSimpleTime(configurationForHours, simpleTimeCalculator, categoryTracker);
		
		System.out.println("Processing test data ...");
		logAnalyzer.process(configurationForDays, new ILogData[] { logDataForWeeksUnderTest, logDataForDaysUnderTest, logDataForHoursUnderTest }, new ProcessingStatusEntity(statusOutputer, "TEST_OUTPUTER"));

		System.out.println("Start assertion tests ...");
	}

	@Test
	public void testConfigurationForWeeks() {

		DateTime nextValue = null;
		for (int serviceId = 1; serviceId <= demoTrafficGenerator.getAmountOfServices(); serviceId++) {
			String functionalityName = "Feature_" + serviceId;
			List<ResultValue[]> values = logDataForWeeksUnderTest.getTable(configurationForDays, functionalityName).getValues();

			int count = 0;
			nextValue = new DateTime(configurationForWeeks.getFrom());
			do {
				Long day = nextValue.getMillis();
				Long dayUnderTest = values.get(count)[0].getValueAsLong();
				
				Long value = getValueFromStruct(getStruct(agregatedWeeks, serviceId), nextValue);
				Long valueUnderTest = values.get(count)[2].getValueAsLong();
				
				Assert.assertEquals(day, dayUnderTest);
				Assert.assertEquals(valueUnderTest, value, "Something wrong for Functionality_" + serviceId + " for date: " + dateTimeFormatter.longToStringWithMinutes(day) + " " + dateTimeFormatter.longToStringWithMinutes(dayUnderTest));
				amountOfAssertions +=2;
				
				++count;
				nextValue = nextValue.plusDays(1);
			} while (nextValue.isBefore(configurationForWeeks.getTo()));

		}
	}

	@Test
	public void testConfigurationForDays() {

		DateTime nextValue = null;
		for (int serviceId = 1; serviceId <= demoTrafficGenerator.getAmountOfServices(); serviceId++) {
			String functionalityName = "Feature_" + serviceId;
			List<ResultValue[]> values = logDataForDaysUnderTest.getTable(configurationForDays, functionalityName).getValues();

			int count = 0;
			nextValue = new DateTime(configurationForDays.getFrom());
			do {
				Long day = nextValue.getMillis();
				Long dayUnderTest = values.get(count)[0].getValueAsLong();
				
				Long value = getValueFromStruct(getStruct(agregatedDays, serviceId), nextValue);
				Long valueUnderTest = values.get(count)[2].getValueAsLong();
				
				Assert.assertEquals(day, dayUnderTest);
				Assert.assertEquals(valueUnderTest, value, "Something wrong for Functionality_" + serviceId + " for date: " + dateTimeFormatter.longToStringWithMinutes(day) + " " + dateTimeFormatter.longToStringWithMinutes(dayUnderTest));
				amountOfAssertions +=2;
				
				++count;
				nextValue = nextValue.plusHours(1);
			} while (nextValue.isBefore(configurationForDays.getTo()));

		}
	}
	
	
	@Test
	public void testConfigurationForHours() {

		DateTime nextValue = null;
		for (int serviceId = 1; serviceId <= demoTrafficGenerator.getAmountOfServices(); serviceId++) {
			String functionalityName = "Feature_" + serviceId;
			List<ResultValue[]> values = logDataForHoursUnderTest.getTable(configurationForDays, functionalityName).getValues();

			int count = 0;
			nextValue = new DateTime(configurationForHours.getFrom());
			do {
				Long day = nextValue.getMillis();
				Long dayUnderTest = values.get(count)[0].getValueAsLong();
				
				Long value = getValueFromStruct(getStruct(agregatedHours, serviceId), nextValue);
				Long valueUnderTest = values.get(count)[2].getValueAsLong();
				
				Assert.assertEquals(day, dayUnderTest, "Something wrong for Functionality_" + serviceId + " for date: " + dateTimeFormatter.longToStringWithMinutes(day) + " " + dateTimeFormatter.longToStringWithMinutes(dayUnderTest));
				Assert.assertEquals(valueUnderTest, value, "Something wrong for Functionality_" + serviceId + " for date: " + dateTimeFormatter.longToStringWithMinutes(dayUnderTest));
				amountOfAssertions +=2;
				
				++count;
				nextValue = nextValue.plusMinutes(1);
			} while (nextValue.isBefore(configurationForHours.getTo()));

		}
	}
	
	@AfterClass
	public void afterClass(){
		System.out.println("" + amountOfAssertions + " assertion was tested.");
	}
	
	private Map<DateTime, Long> getStruct(Map<String, Map<DateTime, Long>> struct, Integer serviceId) {
		String key = "Feature_" + serviceId;
		Map<DateTime, Long> result = struct.get(key);
		if (result == null) {
			result = new HashMap<DateTime, Long>();
			struct.put(key, result);
		}
		return result;
	}

	private Long getValueFromStruct(Map<DateTime, Long> struct, DateTime nextValue) {
		Long value = struct.get(nextValue);
		if (value == null) {
			value = 0L;
		}
		return value;
	}

}
